#include "ServerHttpStatic.h"
#include "ServerProtocol.h"
#include <map>
#include <string>

#ifdef __WINDOWS__
#include "io.h"
#include "direct.h"
#include "resource.h"
#include "corecrt_io.h"
#else
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <unistd.h>

extern char _binary_console_html_start[];
extern char _binary_console_html_end[];
extern char _binary_report_html_start[];
extern char _binary_report_html_end[];
extern char _binary_book_html_start[];
extern char _binary_book_html_end[];
extern char _binary_katex_min_css_start[];
extern char _binary_katex_min_css_end[];
extern char _binary_jquery_min_js_start[];
extern char _binary_jquery_min_js_end[];
extern char _binary_echarts_min_js_start[];
extern char _binary_echarts_min_js_end[];
extern char _binary_logic_js_start[];
extern char _binary_logic_js_end[];
extern char _binary_favicon_ico_start[];
extern char _binary_favicon_ico_end[];
#endif // !__WINDOWS__

static std::map<std::string, std::string> ContentType = {
	{"html","text/html"},
	{"htm","text/html"},
	{"shtml","text/html"},
	{"css","text/css"},
	{"xml","text/xml"},
	{"gif","image/gif"},
	{"jpeg","image/jpeg"},
	{"jpg","image/jpeg"},
	{"js","application/javascript"},
	{"atom","application/atom+xml"},
	{"rss","application/rss+xml"},
	{"mml","text/mathml"},
	{"txt","text/plain"},
	{"jad","text/vnd.sun.j2me.app-descriptor"},
	{"wml","text/vnd.wap.wml"},
	{"htc","text/x-component"},
	{"avif","image/avif"},
	{"png","image/png"},
	{"svg","image/svg+xml"},
	{"svgz","image/svg+xml"},
	{"tif","image/tiff"},
	{"tiff","image/tiff"},
	{"wbmp","image/vnd.wap.wbmp"},
	{"webp","image/webp"},
	{"ico","image/x-icon"},
	{"jng","image/x-jng"},
	{"bmp","image/x-ms-bmp"},
	{"woff","font/woff"},
	{"woff2","font/woff2"},
	{"jar","application/java-archive"},
	{"war","application/java-archive"},
	{"ear","application/java-archive"},
	{"json","application/json"},
	{"hqx","application/mac-binhex40"},
	{"doc","application/msword"},
	{"pdf","application/pdf"},
	{"ps","application/postscript"},
	{"eps","application/postscript"},
	{"ai","application/postscript"},
	{"rtf","application/rtf"},
	{"m3u8","application/vnd.apple.mpegurl"},
	{"kml","application/vnd.google-earth.kml+xml"},
	{"kmz","application/vnd.google-earth.kmz"},
	{"xls","application/vnd.ms-excel"},
	{"eot","application/vnd.ms-fontobject"},
	{"ppt","application/vnd.ms-powerpoint"},
	{"odg","application/vnd.oasis.opendocument.graphics"},
	{"odp","application/vnd.oasis.opendocument.presentation"},
	{"ods","application/vnd.oasis.opendocument.spreadsheet"},
	{"odt","application/vnd.oasis.opendocument.text"},
	{"wmlc","application/vnd.wap.wmlc"},
	{"wasm","application/wasm"},
	{"7z","application/x-7z-compressed"},
	{"cco","application/x-cocoa"},
	{"jardiff","application/x-java-archive-diff"},
	{"jnlp","application/x-java-jnlp-file"},
	{"run","application/x-makeself"},
	{"pl","application/x-perl"},
	{"pm","application/x-perl"},
	{"prc","application/x-pilot"},
	{"pdb","application/x-pilot"},
	{"rar","application/x-rar-compressed"},
	{"rpm","application/x-redhat-package-manager"},
	{"sea","application/x-sea"},
	{"swf","application/x-shockwave-flash"},
	{"sit","application/x-stuffit"},
	{"tcl","application/x-tcl"},
	{"tk","application/x-tcl"},
	{"der","application/x-x509-ca-cert"},
	{"pem","application/x-x509-ca-cert"},
	{"crt","application/x-x509-ca-cert"},
	{"xpi","application/x-xpinstall"},
	{"xhtml","application/xhtml+xml"},
	{"xspf","application/xspf+xml"},
	{"zip","application/zip"},
	{"bin","application/octet-stream"},
	{"exe","application/octet-stream"},
	{"dll","application/octet-stream"},
	{"deb","application/octet-stream"},
	{"dmg","application/octet-stream"},
	{"iso","application/octet-stream"},
	{"img","application/octet-stream"},
	{"msi","application/octet-stream"},
	{"msp","application/octet-stream"},
	{"msm","application/octet-stream"},
	{"mid","audio/midi"},
	{"midi","audio/midi"},
	{"kar","audio/midi"},
	{"mp3","audio/mpeg"},
	{"ogg","audio/ogg"},
	{"m4a","audio/x-m4a"},
	{"ra","audio/x-realaudio"},
	{"3gpp","video/3gpp"},
	{"3gp","video/3gpp"},
	{"ts","video/mp2t"},
	{"mp4","video/mp4"},
	{"mpeg","video/mpeg"},
	{"mpg","video/mpeg"},
	{"mov","video/quicktime"},
	{"webm","video/webm"},
	{"flv","video/x-flv"},
	{"m4v","video/x-m4v"},
	{"mng","video/x-mng"},
	{"asx","video/x-ms-asf"},
	{"asf","video/x-ms-asf"},
	{"wmv","video/x-ms-wmv"},
	{"avi","video/x-msvideo"},
};

static int get_dist_dir_path_files(HSOCKET hsock, const char* path) {
#define HTTP_HEAD_LEN 256
	//char files[8192] = { 0x0 };
	int size = 8192, n, space;
	char* files = (char*)malloc(size), *new_files;
	if (!files) return  -1;
	int offset = HTTP_HEAD_LEN;
#ifdef __WINDOWS__
	char allfile[256] = { 0x0 };
	snprintf(allfile, sizeof(allfile), "%s/*", path);
	intptr_t hFile = 0;
	struct _finddata_t fileinfo;
	if ((hFile = _findfirst(allfile, &fileinfo)) != -1){
		do{
			if ((fileinfo.attrib & _A_SUBDIR)){
				if (strcmp(fileinfo.name, ".") == 0 || strcmp(fileinfo.name, "..") == 0) continue;
			}
			if (size - offset < 512) {
				size = size * 2;
				new_files = (char*)realloc(files, size);
				if (!new_files) goto error;
				files = new_files;
			}
			offset += snprintf(files + offset, size - offset, "<a href=\"%s/%s\" target=\"_blank\">%s</a><br/>", path, fileinfo.name, fileinfo.name);
		} while (_findnext(hFile, &fileinfo) == 0);
		_findclose(hFile);
	}
#else
	DIR* dir;
	struct dirent* ptr;
	if ((dir = opendir(path)) == NULL){
		//perror("Open dir error...");
		return -1;
	}
	while ((ptr = readdir(dir)) != NULL){
		if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0)
			continue;
		if (size - offset < 512) {
			size = size * 2;
			new_files = (char*)realloc(files, size);
			if (!new_files) goto error;
			files = new_files;
		}
		offset += snprintf(files + offset, size - offset, "<a href=\"%s/%s\" target=\"_blank\">%s</a><br/>", path, ptr->d_name, ptr->d_name);
	}
	closedir(dir);
#endif
	offset -= HTTP_HEAD_LEN;
	n = snprintf(files, size, "HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin:*\r\nContent-Type: text/html;charset=UTF-8\r\nContent-Length: %d\r\n\r\n", offset);
	space = HTTP_HEAD_LEN - n;
	memmove(files + space, files, n);
	HsocketSend(hsock, files + space, n + offset);
	free(files);
	return 0;

error:
	free(files);
	return -1;
}

static int get_disk_file(HSOCKET hsock, const char* file) {
	if (strstr(file, "..")) return -1;
	char filename[256];
	snprintf(filename, sizeof(filename), "%s%s", EXE_Path, file);
#ifdef __WINDOWS__
	int ret = _access(filename, 0 | 4);
#else
	int ret = access(filename, 0 | 4);
#endif
	if (ret != 0) {
		if (strcmp(file, "robots.txt") == 0 || strcmp(file, "sitemap.xml") == 0) {
			char buf[64] = { 0x0 };
			int n = snprintf(buf, sizeof(buf), "%s", "HTTP/1.1 404 NOT FOUND\r\n\r\n");
			HsocketSend(hsock, buf, n);
			return 0;
		}
		return 1;
	}
#ifdef __WINDOWS__
#ifdef _WIN64
	struct _stat64 finfo;
	ret = _stat64(filename, &finfo);
#else
	struct _stat32 finfo;
	ret = _stat32(filename, &finfo);
#endif //  _WIN64
#else	//linux
	struct stat finfo;
	ret = stat(filename, &finfo);
#endif
	if (ret < 0) {
		return -1;
	} 
	if (finfo.st_mode & S_IFDIR) {
		return get_dist_dir_path_files(hsock, file);
	}

	FILE* hfile = NULL;
#ifdef __WINDOWS__
	ret = fopen_s(&hfile, filename, "rb");
#else
	hfile = fopen(filename, "rb");
#endif
	if (hfile == NULL) return -1;
	const char* p = NULL;
	const char* s = file;
	while (true)
	{
		const char* m = strchr(s, '.');
		if (m) { p = m; s = p + 1; }
		else break;
	}
	std::map<std::string, std::string>::iterator iter = ContentType.end();
	if (p) iter = ContentType.find(p +1);

	char buf[8192] = { 0x0 };
	int n = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\nCache-Control: no-store\r\nContent-Type: %s\r\nContent-Length:%llu\r\n\r\n", 
		iter != ContentType.end()? iter->second.c_str():"application/octet-stream", (unsigned long long)finfo.st_size);
	HsocketSend(hsock, buf, n);
	while (1){
		n = (int)fread(buf, sizeof(char), sizeof(buf), hfile);
		HsocketSend(hsock, buf, n);
		if ((size_t)n < sizeof(buf)) break;
	}
	fclose(hfile);
	return 0;
}

static int get_report_file(HSOCKET hsock) {
	const char* file = "report";
	if (strstr(file, "..")) return -1;
	char filename[256];
	snprintf(filename, sizeof(filename), "%s%s", EXE_Path, file);
#ifdef __WINDOWS__
	int ret = _access(filename, 0 | 4);
#else
	int ret = access(filename, 0 | 4);
#endif
	if (ret != 0) {
		char buf[64] = { 0x0 };
		int n = snprintf(buf, sizeof(buf), "%s", "HTTP/1.1 200 OK\r\n\r\n");
		HsocketSend(hsock, buf, n);
		return 0;
	}
#ifdef __WINDOWS__
#ifdef _WIN64
	struct _stat64 finfo;
	ret = _stat64(filename, &finfo);
#else
	struct _stat32 finfo;
	ret = _stat32(filename, &finfo);
#endif //  _WIN64
#else	//linux
	struct stat finfo;
	ret = stat(filename, &finfo);
#endif
	if (ret < 0) {
		return -1;
	}
	if (finfo.st_mode & S_IFDIR) {
		return get_dist_dir_path_files(hsock, file);
	}

	FILE* hfile = NULL;
#ifdef __WINDOWS__
	ret = fopen_s(&hfile, filename, "rb");
#else
	hfile = fopen(filename, "rb");
#endif
	if (hfile == NULL) return -1;
	const char* p = NULL;
	const char* s = file;
	while (true)
	{
		const char* m = strchr(s, '.');
		if (m) { p = m; s = p + 1; }
		else break;
	}
	std::map<std::string, std::string>::iterator iter = ContentType.end();
	if (p) iter = ContentType.find(p + 1);

	char buf[8192] = { 0x0 };
	int n = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\nCache-Control: no-store\r\nContent-Type: %s\r\nContent-Length:%llu\r\n\r\n",
		iter != ContentType.end() ? iter->second.c_str() : "application/octet-stream", (unsigned long long)finfo.st_size);
	HsocketSend(hsock, buf, n);
	while (1) {
		n = (int)fread(buf, sizeof(char), sizeof(buf), hfile);
		HsocketSend(hsock, buf, n);
		if ((size_t)n < sizeof(buf)) break;
	}
	fclose(hfile);
	return 0;
}

static int get_default_home_page(HSOCKET hsock, bool default_page)
{
	const char* home = config_get_string_value("server", "home_page", NULL);
	if (!default_page && home) {
		//return get_static_file(hsock, home);
		char buf[256] = { 0x0 };
		int offset = snprintf(buf, sizeof(buf), "HTTP/1.1 302 Found\r\nLocation: %s\r\n\r\n", home);
		HsocketSend(hsock, buf, offset);
		return 0;
	}
		
	const char* file = NULL;
	int flen = 0;

#ifdef __WINDOWS__
	HRSRC rc = FindResource(Sheeps_Module, MAKEINTRESOURCE(IDR_HTML1), RT_HTML);
	if (rc == NULL) {
		return -1;
	}
	HGLOBAL  hGlobal = LoadResource(Sheeps_Module, rc);
	if (NULL == hGlobal) {
		return -1;
	}
	file = (char*)LockResource(hGlobal);
	flen = SizeofResource(Sheeps_Module, rc);
#else
	flen = _binary_console_html_end - _binary_console_html_start;
	file = _binary_console_html_start;
#endif
	char buf[128] = { 0x0 };
	int offset = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\nCache-Control: public,max-age=86400\r\nContent-Length:%d\r\n\r\n", flen);

	HsocketSend(hsock, buf, offset);
	HsocketSend(hsock, file, flen);
	return 0;
}

static int get_report_template_page(HSOCKET hsock)
{
	const char* file = NULL;
	int flen = 0;

#ifdef __WINDOWS__
	HRSRC rc = FindResource(Sheeps_Module, MAKEINTRESOURCE(IDR_HTML2), RT_HTML);
	if (rc == NULL) {
		return -1;
	}
	HGLOBAL  hGlobal = LoadResource(Sheeps_Module, rc);
	if (NULL == hGlobal) {
		return -1;
	}
	file = (char*)LockResource(hGlobal);
	flen = SizeofResource(Sheeps_Module, rc);
#else
	flen = _binary_report_html_end - _binary_report_html_start;
	file = _binary_report_html_start;
#endif
	char buf[128] = { 0x0 };
	int offset = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nCache-Control: public,max-age=86400\r\nContent-Length:%d\r\n\r\n", flen);

	HsocketSend(hsock, buf, offset);
	HsocketSend(hsock, file, flen);
	return 0;
}

static int get_handld_book_page(HSOCKET hsock)
{
	const char* file = NULL;
	int flen = 0;

#ifdef __WINDOWS__
	HRSRC rc = FindResource(Sheeps_Module, MAKEINTRESOURCE(IDR_HTML3), RT_HTML);
	if (rc == NULL) {
		return -1;
	}
	HGLOBAL  hGlobal = LoadResource(Sheeps_Module, rc);
	if (NULL == hGlobal) {
		return -1;
	}
	file = (char*)LockResource(hGlobal);
	flen = SizeofResource(Sheeps_Module, rc);
#else
	flen = _binary_book_html_end - _binary_book_html_start;
	file = _binary_book_html_start;
#endif
	char buf[128] = { 0x0 };
	int offset = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nCache-Control: public,max-age=86400\r\nContent-Length:%d\r\n\r\n", flen);

	HsocketSend(hsock, buf, offset);
	HsocketSend(hsock, file, flen);
	return 0;
}

static int get_query_lib(HSOCKET hsock)
{
	const char* file = NULL;
	int flen = 0;

#ifdef __WINDOWS__
	HRSRC rc = FindResourceA(Sheeps_Module, MAKEINTRESOURCEA(IDR_JS1), "JS");
	if (rc == NULL) {
		return -1;
	}
	HGLOBAL  hGlobal = LoadResource(Sheeps_Module, rc);
	if (NULL == hGlobal) {
		return -1;
	}
	file = (const char*)LockResource(hGlobal);
	flen = SizeofResource(Sheeps_Module, rc);
#else
	flen = _binary_jquery_min_js_end - _binary_jquery_min_js_start;
	file = _binary_jquery_min_js_start;
#endif // __WINDOWS__
	char buf[128] = { 0x0 };
	int offset = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\nCache-Control: public,max-age=86400\r\nContent-Type: application/javascript\r\nContent-Length:%d\r\n\r\n", flen);

	HsocketSend(hsock, buf, offset);
	HsocketSend(hsock, file, flen);
	return 0;
}

static int get_echarts_lib(HSOCKET hsock)
{
	const char* file = NULL;
	int flen = 0;

#ifdef __WINDOWS__
	HRSRC rc = FindResourceA(Sheeps_Module, MAKEINTRESOURCEA(IDR_JS2), "JS");
	if (rc == NULL) {
		return -1;
	}
	HGLOBAL  hGlobal = LoadResource(Sheeps_Module, rc);
	if (NULL == hGlobal) {
		return -1;
	}
	file = (const char*)LockResource(hGlobal);
	flen = SizeofResource(Sheeps_Module, rc);
#else
	flen = _binary_echarts_min_js_end - _binary_echarts_min_js_start;
	file = _binary_echarts_min_js_start;
#endif // __WINDOWS__
	char buf[128] = { 0x0 };
	int offset = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\nCache-Control: public,max-age=86400\r\nContent-Type: application/javascript\r\nContent-Length:%d\r\n\r\n", flen);

	HsocketSend(hsock, buf, offset);
	HsocketSend(hsock, file, flen);
	return 0;
}

static int get_js_file(HSOCKET hsock)
{
	const char* file = NULL;
	int flen = 0;

#ifdef __WINDOWS__
	HRSRC rc = FindResourceA(Sheeps_Module, MAKEINTRESOURCEA(IDR_JS3), "JS");
	if (rc == NULL) {
		return -1;
	}
	HGLOBAL  hGlobal = LoadResource(Sheeps_Module, rc);
	if (NULL == hGlobal) {
		return -1;
	}
	file = (const char*)LockResource(hGlobal);
	flen = SizeofResource(Sheeps_Module, rc);
#else
	flen = _binary_logic_js_end - _binary_logic_js_start;
	file = _binary_logic_js_start;
#endif // __WINDOWS__
	char buf[128] = { 0x0 };
	int offset = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\nCache-Control: public,max-age=86400\r\nContent-Type: application/javascript\r\nContent-Length:%d\r\n\r\n", flen);

	HsocketSend(hsock, buf, offset);
	HsocketSend(hsock, file, flen);
	return 0;
}

static int get_css_file(HSOCKET hsock)
{
	const char* file = NULL;
	int flen = 0;

#ifdef __WINDOWS__
	HRSRC rc = FindResourceA(Sheeps_Module, MAKEINTRESOURCEA(IDR_CSS1), "CSS");
	if (rc == NULL) {
		return -1;
	}
	HGLOBAL  hGlobal = LoadResource(Sheeps_Module, rc);
	if (NULL == hGlobal) {
		return -1;
	}
	file = (const char*)LockResource(hGlobal);
	flen = SizeofResource(Sheeps_Module, rc);
#else
	flen = _binary_katex_min_css_end - _binary_katex_min_css_start;
	file = _binary_katex_min_css_start;
#endif // __WINDOWS__
	char buf[128] = { 0x0 };
	int offset = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\nCache-Control: public,max-age=86400\r\nContent-Type: text/css\r\nContent-Length:%d\r\n\r\n", flen);

	HsocketSend(hsock, buf, offset);
	HsocketSend(hsock, file, flen);
	return 0;
}

static int get_favicon(HSOCKET hsock)
{
	const char* file = NULL;
	int flen = 0;

#ifdef __WINDOWS__
	HRSRC rc = FindResourceA(Sheeps_Module, MAKEINTRESOURCEA(IDI_ICON1), "ICO");
	if (rc == NULL) {
		return -1;
	}
	HGLOBAL  hGlobal = LoadResource(Sheeps_Module, rc);
	if (NULL == hGlobal) {
		return -1;
	}

	file = (const char*)LockResource(hGlobal);
	flen = SizeofResource(Sheeps_Module, rc);
#else
	flen = _binary_favicon_ico_end - _binary_favicon_ico_start;
	file = _binary_favicon_ico_start;
#endif // __WINDOWS__
	char buf[128] = { 0x0 };
	int offset = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\nCache-Control: public,max-age=86400\r\nContent-Type: image/x-icon\r\nContent-Length:%d\r\n\r\n", flen);

	HsocketSend(hsock, buf, offset);
	HsocketSend(hsock, file, flen);
	return 0;
}

static int get_supersheeps_ca_pem_crt(HSOCKET hsock)
{
	const char* file = ca_crt;
	int flen = (int)strlen(ca_crt);

	char buf[128] = { 0x0 };
	int offset = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\nCache-Control: public,max-age=86400\r\nContent-Type: application/x-x509-ca-cert\r\nContent-Length:%d\r\n\r\n", flen);

	HsocketSend(hsock, buf, offset);
	HsocketSend(hsock, file, flen);
	return 0;
}

#ifdef __WINDOWS__
#include "io.h"
#include "direct.h"
static int get_sheeps_zip(HSOCKET hsock)
{
	const char* filename = "./sheeps.zip";
	int ret = 0;
#ifdef _WIN64
	struct _stat64 finfo;
	ret = _stat64(filename, &finfo);
#else
	struct _stat32 finfo;
	ret = _stat32(filename, &finfo);
#endif //  _WIN64

	if (ret < 0)
		return -1;

	char* data = (char*)malloc(finfo.st_size);
	if (data == NULL)
		return -1;
	memset(data, 0, finfo.st_size);

	FILE* file = NULL;
	ret = fopen_s(&file, filename, "rb");
	if (file == NULL)
		return -1;
	fread(data, sizeof(char), finfo.st_size, file);
	fclose(file);

	char buf[128] = { 0x0 };
	int offset = snprintf(buf, sizeof(buf), "HTTP/1.1 200 OK\r\nCache-Control: no-store\r\nContent-Type: application/zip\r\nContent-Length:%llu\r\n\r\n", (unsigned long long)finfo.st_size);
	HsocketSend(hsock, buf, offset);
	HsocketSend(hsock, data, (int)finfo.st_size);
	free(data);
	return 0;
}
#endif // __WINDOWS__

int get_static_file(HSOCKET hsock, const char* uri)
{
	if (strcmp(uri, "/") == 0)
		return get_default_home_page(hsock, false);
	else if (strcmp(uri, "/console") == 0)
		return get_default_home_page(hsock, true);
	else if (strcmp(uri, "/report_template") == 0)
		return get_report_template_page(hsock);
	else if (strcmp(uri, "/SuperSheeps使用手册.html") == 0)
		return get_handld_book_page(hsock);
	else if (strcmp(uri, "/jquery.min.js") == 0)
		return get_query_lib(hsock);
	else if (strcmp(uri, "/echarts.min.js") == 0)
		return get_echarts_lib(hsock);
	else if (strcmp(uri, "/logic.js") == 0)
		return get_js_file(hsock);
	else if (strcmp(uri, "/katex.min.css") == 0)
		return get_css_file(hsock);
	else if (strcmp(uri, "/favicon.ico") == 0)
		return get_favicon(hsock);
	else if (strcmp(uri, "/supersheeps_ca_pem.crt") == 0)
		return get_supersheeps_ca_pem_crt(hsock);
	else if (strcmp(uri, "/report") == 0)
		return get_report_file(hsock);
#ifdef __WINDOWS__
	else if (strcmp(uri, "/sheeps.zip") == 0)
		return get_sheeps_zip(hsock);
#endif // __WINDOWS__
	else
		return get_disk_file(hsock, uri + 1);
	return 1;
}